#pragma once
#include <dialects/Memory/Dialect.h>
#include <lcir/IR/ExprTemps.h>
#include <lcir/IR/Op.h>

#include <cstddef>
#include <vector>

#include "dialects/Builtin/Prototype.h"
#include "dialects/Builtin/Types.h"
#include "lcir/IR/Prototype.h"
using namespace std::literals;
namespace lcir::memory {
// alloca load store
class Alloca : public OpCRTP<Alloca, MemoryDialect> {
  Type *elementType{nullptr};
  size_t allocSize{0};  // 记录分配内存块的大小
  std::string varName;

 public:
  Alloca(Type *type) : OpCRTP("Alloca"), elementType(type) {}
  Alloca(Type *type, std::string_view varName) : OpCRTP("Alloca"), elementType(type) {}
  Alloca(Type *type, std::string_view varName, size_t allocSize)
      : OpCRTP("Alloca"), elementType(type), allocSize(allocSize) {}
  Type *getElemenmType() const { return elementType; }
  bool checkType(const std::vector<lcir::Prototype *> &operandVals) override { return operandVals.size() == 0; }
  std::vector<lcir::Prototype *> deduceRetType(const std::vector<lcir::Prototype *> &operandProtos) override {
    auto *ret = new builtin::PointerVarProto(elementType);
    std::stringstream ss;
    ss << varName;
    if (allocSize) ss << "[" << allocSize << "]";
    ret->setInfo(ss.str());
    return {ret};
  }
};
class Load : public OpCRTP<Load, MemoryDialect> {
 public:
  Load() : OpCRTP("Load") {}
  bool checkType(const std::vector<lcir::Prototype *> &operandVals) override {
    return operandVals.size() == 1 && operandVals[0]->getType()->isa<builtin::Pointer>();
  }
  std::vector<lcir::Prototype *> deduceRetType(const std::vector<lcir::Prototype *> &operandProtos) override {
    auto eleType = operandProtos[0]->getType()->dyn_cast<builtin::Pointer>()->getElementType();
    if (eleType->isa<builtin::FloatType>()) {
      return {new builtin::FloatVarProto()};
    }
    if (eleType->isa<builtin::Int32Type>()) {
      return {new builtin::Int32VarProto()};
    }
    return {new builtin::UnkownVarProto()};
  }
};
/**
 * @brief 赋值操作，操作数: 左操作数是要赋的值， 右操作数是目标的变量，
 */
class Store : public OpCRTP<Store, MemoryDialect, LiveNode> {
 public:
  Store() : OpCRTP("Store") {}
  bool checkType(const std::vector<lcir::Prototype *> &operandVals) override {
    return operandVals.size() == 2 && operandVals[1]->getType()->isa<builtin::Pointer>();
  }
  std::vector<lcir::Prototype *> deduceRetType(const std::vector<lcir::Prototype *> &operandProtos) override {
    return {};
  }
};
/**
 * @brief 这个算子从指定地址load一个变量，并且加上地址偏移( 这个好还是GEP好？)
 */
class OffsetLoad : public OpCRTP<OffsetLoad, MemoryDialect> {
 public:
  OffsetLoad() : OpCRTP("OffsetLoad") {}
  bool checkType(const std::vector<lcir::Prototype *> &operandVals) override {
    return operandVals[0]->getType()->isa<builtin::Pointer>();
  }
  std::vector<lcir::Prototype *> deduceRetType(const std::vector<lcir::Prototype *> &operandProtos) override {
    auto eleType = operandProtos[0]->getType()->dyn_cast<builtin::Pointer>()->getElementType();
    if (eleType->isa<builtin::FloatType>()) {
      return {new builtin::FloatVarProto()};
    }
    if (eleType->isa<builtin::Int32Type>()) {
      return {new builtin::Int32VarProto()};
    }
    return {new builtin::UnkownVarProto()};
  }
};

/**
 * @brief 计算地址偏移
 */
class OffsetPtr : public OpCRTP<OffsetPtr, MemoryDialect> {
 public:
  OffsetPtr() : OpCRTP("OffsetPtr") {}
  bool checkType(const std::vector<lcir::Prototype *> &operandVals) override {
    return operandVals.size() >= 1 && operandVals[0]->getType()->isa<builtin::Pointer>();
  }
  std::vector<lcir::Prototype *> deduceRetType(const std::vector<lcir::Prototype *> &operandProtos) override {
    auto eleType = operandProtos[0]->getType();
    return {new builtin::PointerVarProto(eleType)};
  }
};

}  // namespace lcir::memory